package converter

import (
	"archive/zip"
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/SebastiaanKlippert/go-wkhtmltopdf"
)

func init() {
	// 从环境变量读取wkhtmltopdf路径
	if path := os.Getenv("WKHTMLTOPDF_PATH"); path != "" {
		wkhtmltopdf.SetPath(path)
	}
}

// OPF 结构体定义
type OPF struct {
	XMLName  xml.Name `xml:"package"`
	Version  string   `xml:"version,attr"`
	Spine    Spine    `xml:"spine"`
	Manifest Manifest `xml:"manifest"`
}

type Spine struct {
	ItemRefs []ItemRef `xml:"itemref"`
}

type ItemRef struct {
	IDRef string `xml:"idref,attr"`
}

type Manifest struct {
	Items []Item `xml:"item"`
}

type Item struct {
	ID        string `xml:"id,attr"`
	Href      string `xml:"href,attr"`
	MediaType string `xml:"media-type,attr"`
}

// Converter 定义转换器接口
type Converter interface {
	Convert(inputPath, outputPath string) error
}

// EPUBConverter EPUB转PDF转换器
type EPUBConverter struct {
	// 可以添加配置选项
}

// NewEPUBConverter 创建新的EPUB转换器实例
func NewEPUBConverter() *EPUBConverter {
	return &EPUBConverter{}
}

// Convert 实现转换方法
func (c *EPUBConverter) Convert(inputPath, outputPath string) error {
	startTime := time.Now()
	fmt.Printf("开始处理文件: %s\n", inputPath)

	// 检查输入文件是否存在
	if _, err := os.Stat(inputPath); os.IsNotExist(err) {
		fmt.Printf("错误: 输入文件不存在: %s\n", inputPath)
		os.Exit(1)
	}

	// 在输入文件所在目录创建临时目录
	inputDir := filepath.Dir(inputPath)
	tempDir := filepath.Join(inputDir, "epub-convert-temp")
	if err := os.MkdirAll(tempDir, 0755); err != nil {
		fmt.Printf("错误: 创建临时目录失败: %v\n", err)
		os.Exit(1)
	}
	defer os.RemoveAll(tempDir)

	fmt.Printf("创建临时目录: %s\n", tempDir)

	// 打开EPUB文件（EPUB本质上是一个ZIP文件）
	reader, err := zip.OpenReader(inputPath)
	if err != nil {
		fmt.Printf("错误: 无法打开EPUB文件: %v\n", err)
		os.Exit(1)
	}
	defer reader.Close()

	// 解压EPUB文件
	if err := extractEPUB(reader, tempDir); err != nil {
		fmt.Printf("错误: 解压EPUB文件失败: %v\n", err)
		os.Exit(1)
	}

	// 查找OPF文件
	opfPath, err := findOPFFile(tempDir)
	if err != nil {
		fmt.Printf("错误: 查找OPF文件失败: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("找到OPF文件: %s\n", opfPath)

	// 解析OPF文件获取HTML文件列表
	htmlFiles, err := parseOPFFile(opfPath)
	if err != nil {
		fmt.Printf("错误: 解析OPF文件失败: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("找到 %d 个HTML文件\n", len(htmlFiles))

	// 创建临时目录用于存储单页PDF
	pdfTempDir := filepath.Join(tempDir, "pdfs")
	if err := os.MkdirAll(pdfTempDir, 0755); err != nil {
		fmt.Printf("错误: 创建PDF临时目录失败: %v\n", err)
		os.Exit(1)
	}

	// 创建工作池
	workerCount := 8 // 工作线程数
	jobs := make(chan int, len(htmlFiles))
	results := make(chan error, len(htmlFiles))
	var wg sync.WaitGroup
	var processedCount int32

	// 启动工作线程
	for i := 0; i < workerCount; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			for index := range jobs {
				htmlPath := htmlFiles[index]
				pdfPath := filepath.Join(pdfTempDir, fmt.Sprintf("page_%04d.pdf", index))

				// 读取HTML文件内容
				htmlContent, err := os.ReadFile(filepath.Join(tempDir, htmlPath))
				if err != nil {
					results <- fmt.Errorf("读取HTML文件失败 [%s]: %v", htmlPath, err)
					continue
				}

				// 处理HTML内容，确保编码正确
				htmlStr := string(htmlContent)

				// 如果HTML没有指定编码，添加UTF-8编码声明
				if !strings.Contains(strings.ToLower(htmlStr), "charset") {
					htmlStr = strings.Replace(htmlStr, "<head>", "<head><meta charset=\"utf-8\">", 1)
				}

				// 添加字体样式，使用系统默认字体
				if !strings.Contains(htmlStr, "font-family") {
					htmlStr = strings.Replace(htmlStr, "<head>", `<head><style>
						body { font-family: "Microsoft YaHei", "SimSun", "SimHei", Arial, sans-serif; }
						* { font-family: inherit; }
					</style>`, 1)
				}

				// 为每个页面创建新的PDF生成器
				pdfg, err := wkhtmltopdf.NewPDFGenerator()
				if err != nil {
					results <- fmt.Errorf("创建PDF生成器失败 [%s]: %v", htmlPath, err)
					continue
				}

				// 设置PDF选项
				pdfg.Dpi.Set(300)
				pdfg.Orientation.Set(wkhtmltopdf.OrientationPortrait)
				pdfg.Grayscale.Set(false)
				pdfg.PageSize.Set(wkhtmltopdf.PageSizeA4)
				pdfg.MarginTop.Set(0)
				pdfg.MarginBottom.Set(0)
				pdfg.MarginLeft.Set(0)
				pdfg.MarginRight.Set(0)

				// 创建页面
				page := wkhtmltopdf.NewPageReader(bytes.NewReader([]byte(htmlStr)))
				page.EnableLocalFileAccess.Set(true)
				page.LoadErrorHandling.Set("ignore")
				page.LoadMediaErrorHandling.Set("ignore")
				page.JavascriptDelay.Set(1000)
				page.NoStopSlowScripts.Set(true)

				// 设置字体和编码选项
				page.Encoding.Set("UTF-8")

				// 添加页面到生成器
				pdfg.AddPage(page)

				// 生成PDF
				err = pdfg.Create()
				if err != nil {
					results <- fmt.Errorf("生成PDF失败 [%s]: %v", htmlPath, err)
					continue
				}

				// 保存PDF文件
				err = pdfg.WriteFile(pdfPath)
				if err != nil {
					results <- fmt.Errorf("保存PDF文件失败 [%s]: %v", htmlPath, err)
					continue
				}

				// 验证PDF文件是否成功保存
				if fi, err := os.Stat(pdfPath); err != nil || fi.Size() == 0 {
					results <- fmt.Errorf("PDF文件保存失败或为空 [%s]", htmlPath)
					continue
				}

				// 更新进度
				atomic.AddInt32(&processedCount, 1)
				current := atomic.LoadInt32(&processedCount)
				fmt.Printf("\r处理进度: %d/%d (%.1f%%)", current, len(htmlFiles), float64(current)/float64(len(htmlFiles))*100)
			}
		}()
	}

	// 分发任务
	for i := range htmlFiles {
		jobs <- i
	}
	close(jobs)

	// 等待所有工作线程完成
	go func() {
		wg.Wait()
		close(results)
		fmt.Println() // 换行
	}()

	// 收集错误
	for err := range results {
		if err != nil {
			fmt.Printf("\n错误: %v\n", err)
			os.Exit(1)
		}
	}

	// 合并所有PDF文件
	fmt.Println("正在合并PDF文件...")
	var pdfFiles []string
	for i := 0; i < len(htmlFiles); i++ {
		pdfPath := filepath.Join(pdfTempDir, fmt.Sprintf("page_%04d.pdf", i))
		if fi, err := os.Stat(pdfPath); err == nil && fi.Size() > 0 {
			pdfFiles = append(pdfFiles, pdfPath)
		} else {
			fmt.Printf("警告: PDF文件不存在或为空: %s\n", pdfPath)
		}
	}

	if len(pdfFiles) == 0 {
		fmt.Printf("错误: 没有有效的PDF文件可以合并\n")
		os.Exit(1)
	}

	// 创建输出文件
	outFile, err := os.Create(outputPath)
	if err != nil {
		fmt.Printf("错误: 创建输出文件失败: %v\n", err)
		os.Exit(1)
	}
	defer outFile.Close()

	// 合并PDF文件
	for i, pdfFile := range pdfFiles {
		// 读取PDF文件
		pdfBytes, err := os.ReadFile(pdfFile)
		if err != nil {
			fmt.Printf("错误: 读取PDF文件失败 [%s]: %v\n", pdfFile, err)
			os.Exit(1)
		}

		// 检查PDF内容
		if len(pdfBytes) == 0 {
			fmt.Printf("错误: PDF文件内容为空 [%s]\n", pdfFile)
			os.Exit(1)
		}

		// 写入输出文件
		if i > 0 {
			// 跳过PDF文件头
			start := bytes.Index(pdfBytes, []byte("%PDF-"))
			if start > 0 {
				pdfBytes = pdfBytes[start:]
			}
		}

		if _, err := outFile.Write(pdfBytes); err != nil {
			fmt.Printf("错误: 写入PDF文件失败: %v\n", err)
			os.Exit(1)
		}
	}

	// 验证输出文件
	if fi, err := os.Stat(outputPath); err != nil || fi.Size() == 0 {
		fmt.Printf("错误: 输出文件创建失败或为空\n")
		os.Exit(1)
	}

	// 输出文件大小
	if fileInfo, err := os.Stat(outputPath); err == nil {
		fmt.Printf("输出文件大小: %.2f MB\n", float64(fileInfo.Size())/1024/1024)
	}

	// 输出总耗时
	fmt.Printf("转换完成，总耗时: %.2f 秒\n", time.Since(startTime).Seconds())

	return nil
}

// extractEPUB 解压EPUB文件
func extractEPUB(reader *zip.ReadCloser, tempDir string) error {
	fmt.Printf("开始解压EPUB文件到: %s\n", tempDir)
	for _, file := range reader.File {
		// 跳过目录
		if file.FileInfo().IsDir() {
			continue
		}

		// 创建目标文件
		targetPath := filepath.Join(tempDir, file.Name)
		if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
			return fmt.Errorf("创建目录失败: %v", err)
		}

		// 打开源文件
		src, err := file.Open()
		if err != nil {
			return fmt.Errorf("打开文件失败: %v", err)
		}
		defer src.Close()

		// 创建目标文件
		dst, err := os.Create(targetPath)
		if err != nil {
			return fmt.Errorf("创建文件失败: %v", err)
		}
		defer dst.Close()

		// 复制文件内容
		if _, err := io.Copy(dst, src); err != nil {
			return fmt.Errorf("复制文件失败: %v", err)
		}
	}
	fmt.Println("EPUB文件解压完成")
	return nil
}

// findOPFFile 查找OPF文件
func findOPFFile(tempDir string) (string, error) {
	// 首先检查META-INF/container.xml
	containerPath := filepath.Join(tempDir, "META-INF", "container.xml")
	if _, err := os.Stat(containerPath); err == nil {
		// 读取container.xml
		content, err := os.ReadFile(containerPath)
		if err != nil {
			return "", fmt.Errorf("读取container.xml失败: %v", err)
		}

		// 解析XML
		type Container struct {
			RootFiles []struct {
				FullPath string `xml:"full-path,attr"`
			} `xml:"rootfiles>rootfile"`
		}
		var container Container
		if err := xml.Unmarshal(content, &container); err != nil {
			return "", fmt.Errorf("解析container.xml失败: %v", err)
		}

		// 获取OPF文件路径
		if len(container.RootFiles) > 0 {
			opfPath := filepath.Join(tempDir, container.RootFiles[0].FullPath)
			if _, err := os.Stat(opfPath); err == nil {
				return opfPath, nil
			}
		}
	}

	// 如果找不到container.xml，尝试直接查找.opf文件
	var opfPath string
	err := filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".opf") {
			opfPath = path
			return filepath.SkipAll
		}
		return nil
	})

	if err != nil {
		return "", fmt.Errorf("查找OPF文件失败: %v", err)
	}

	if opfPath == "" {
		return "", fmt.Errorf("未找到OPF文件")
	}

	return opfPath, nil
}

// parseOPFFile 解析OPF文件获取HTML文件列表
func parseOPFFile(opfPath string) ([]string, error) {
	// 读取OPF文件
	content, err := os.ReadFile(opfPath)
	if err != nil {
		return nil, fmt.Errorf("读取OPF文件失败: %v", err)
	}

	// 解析XML
	var opf OPF
	if err := xml.Unmarshal(content, &opf); err != nil {
		return nil, fmt.Errorf("解析OPF文件失败: %v", err)
	}

	// 创建ID到Href的映射
	idToHref := make(map[string]string)
	for _, item := range opf.Manifest.Items {
		if strings.HasPrefix(item.MediaType, "application/xhtml+xml") ||
			strings.HasPrefix(item.MediaType, "text/html") {
			idToHref[item.ID] = item.Href
		}
	}

	// 按照spine顺序获取HTML文件
	var htmlFiles []string
	for _, itemRef := range opf.Spine.ItemRefs {
		if href, ok := idToHref[itemRef.IDRef]; ok {
			htmlFiles = append(htmlFiles, href)
		}
	}

	return htmlFiles, nil
}
